package com.barry.auth.filter;

import com.barry.auth.model.ResultData;
import com.barry.auth.model.ReturnCode;
import com.barry.auth.util.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.barry.auth.model.RequestHeader;

import java.util.SortedMap;

@Slf4j
public class SignInterceptor implements HandlerInterceptor {

    private RedisUtil redisUtil;

    //从fitler配置中获取sign过期时间
    private Long signMaxTime;
    private static final String NONCE_KEY = "x-nonce-";

    @Override
    public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Object handler) throws Exception {

        HttpServletRequestWrapper requestWrapper = new SignRequestWrapper(httpRequest);

        RequestHeader requestHeader = new RequestHeader();
        requestHeader.setNonce(httpRequest.getHeader("x-Nonce"));
        requestHeader.setSign(httpRequest.getHeader("X-Sign"));
        String header = httpRequest.getHeader("X-Time");
        if (StringUtils.isEmpty(header)) {
            responseFail(httpResponse, ReturnCode.ILLEGAL_HEADER);
            return false;
        }
        requestHeader.setTimestamp(Long.parseLong(header));
        //验证请求头是否存在
        if (StringUtils.isEmpty(requestHeader.getSign())
                || ObjectUtils.isEmpty(requestHeader.getTimestamp())
                || StringUtils.isEmpty(requestHeader.getNonce())) {
            responseFail(httpResponse, ReturnCode.ILLEGAL_HEADER);
            return false;
        }
        /*
         * 1.重放验证
         * 判断timestamp时间戳与当前时间是否超过60s（过期时间根据业务情况设置）,如果超过了就提示签名过期。
         */
        long now = System.currentTimeMillis() / 1000;

        if (now - requestHeader.getTimestamp() > signMaxTime) {
            responseFail(httpResponse, ReturnCode.REPLAY_ERROR);
            return false;
        }

        //2. 判断nonce
        boolean nonceExists = redisUtil.hasKey(NONCE_KEY + requestHeader.getNonce());
        if (nonceExists) {
            //请求重复
            responseFail(httpResponse, ReturnCode.REPLAY_ERROR);
            return false;
        } else {
            redisUtil.set(NONCE_KEY + requestHeader.getNonce(), requestHeader.getNonce(), signMaxTime);
        }

        boolean accept;
        SortedMap<String, String> paramMap;
        switch (httpRequest.getMethod()) {
            case "GET":
                paramMap = HttpDataUtil.getUrlParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            case "POST":
                paramMap = HttpDataUtil.getBodyParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            default:
                accept = true;
                break;
        }
        if (accept) {
            return true;
        } else {
            responseFail(httpResponse, ReturnCode.ARGUMENT_ERROR);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    private void responseFail(HttpServletResponse httpResponse, ReturnCode returnCode) {
        ResultData<Object> resultData = ResultData.fail(returnCode.getCode(), returnCode.getMessage());
        WebUtils.writeJson(httpResponse, resultData);
    }

}